package Person;
 
use Moose;
 

# accessor 默认就是Attribute的名字
has 'first_name' => ( is => 'rw' );

# 也可以自定义accessor的名字，推荐使用
has 'weight' => (
    is     => 'rw',
    reader => 'get_weight',
    writer => 'set_weight',
);

# 还可以定义predicate和clearer methods
# predicate就是判断Attribute有没有被设置过，即使设置成undef也可以识别
# clearer可以将Attribute清除，跟手动设置成undef是不一样的

has 'ssn' => (
    is        => 'rw',
    clearer   => 'clear_ssn',
    predicate => 'has_ssn',
);
 
...
 
my $person = Person->new();
$person->has_ssn; # false
 
$person->ssn(undef);
$person->ssn; # returns undef
$person->has_ssn; # true
 
$person->clear_ssn;
$person->ssn; # returns undef
$person->has_ssn; # false
 
$person->ssn('123-45-6789');
$person->ssn; # returns '123-45-6789'
$person->has_ssn; # true
 
my $person2 = Person->new( ssn => '111-22-3333');
$person2->has_ssn; # true

# 如果一个必须的Attribute，可以加上require，在对象初始化的时候必须要被赋值，但是之后可以被清除
has 'name' => (
    is       => 'ro',
    required => 1,
);

###################################################################################################
# 怎么设置Attribute的默认值
# 方法1，直接传一个非引用常量
has 'size' => (
    is        => 'ro',
    default   => 'medium',
    predicate => 'has_size',
);

# 方法2 通过subroutine reference返回，每次对象创建的时候都会调用这个subroutine
has 'size' => (
    is => 'ro',
    default =>
        sub { ( 'small', 'medium', 'large' )[ int( rand 3 ) ] },
    predicate => 'has_size',
);

# 这个subroutine会被当作是这个对象的method调用，所以对象本身会成为参数
# 需要注意的是调用subroutine的时候，其它的Attribute可能还没有被设置
has 'size' => (
    is      => 'ro',
    default => sub {
        my $self = shift;
 
        return $self->height > 200 ? 'large' : 'average';
    },
);

# 如果想给Attribute设置reference，必须用subroutine返回
has 'mapping' => (
    is      => 'ro',
    default => sub { {} },
);

# 方法3 ，使用builder method，推荐使用
has 'size' => (
    is        => 'ro',
    builder   => '_build_size',
    predicate => 'has_size',
);
 
sub _build_size {
    return ( 'small', 'medium', 'large' )[ int( rand 3 ) ];
}


# 如果一个Attribute需要依赖别的条件才能初始化，可以定义lazy，或者这个Attribute不一定需要，不用浪费时间初始化
# 只有当第一次访问它的时候才会被初始化
has 'size' => (
    is      => 'ro',
    lazy    => 1,
    builder => '_build_size',
);


###################################################################################################
# 可以不让通过constructor初始化

has '_genetic_code' => (
    is       => 'ro',
    lazy     => 1,
    builder  => '_build_genetic_code',
    init_arg => undef,
);

# 可以更改constructor的参数名称
has 'bigness' => (
    is       => 'ro',
    init_arg => 'size',
);

###################################################################################################
# 子类可以修改父类Attribute的属性，前面加个+
# 但是关联的methods不能修改
package LazyPerson;
 
use Moose;
 
extends 'Person';
 
has '+first_name' => (
    lazy    => 1,
    default => 'Bill',
);

###################################################################################################
# 可以限制Attribute的类型
has 'first_name' => (
    is  => 'ro',
    isa => 'Str',
);

has 'hair_color' => (
    is      => 'ro',
    isa     => 'Graphics::Color::RGB',
);

###################################################################################################
sub eat {
	my $self = shift;

	# do the eating
}

###################################################################################################
# 继承
package User;
 
use Moose;
 
extends 'Person';
 
has 'username' => ( is => 'rw' );


###################################################################################################
# 实例化
use Person;
 
my $person = Person->new(
  # attribute values at instantiation
  # go here
  ssn => '123456789',
);